﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using System.Windows.Forms;

using TLL.Repository;
using TLL.UTL;
using TLL.Debug;

namespace TLL.Log
{

    public class LogRepository : XMLRepository<LogItem>
    {
        private int lastID = 0;
        public LogRepository(string dataURL, string debugURL, NotifyIcon notify) : base(dataURL, debugURL, true, true, XMLDirectionType.Local, notify) { this.lastID = this.GetLastID(); }

        public override List<LogItem> Get(int id = 0)
        {
            var res = new List<LogItem>();
            List<LogProject> projects = new List<LogProject>();
            using (var repository = new LogProjectRepository("Data\\Projects.data", this.DebugURL, this.Notify))
                projects = repository.Get();
            try
            {
                if (id == 0)
                    foreach (XmlNode node in this.Document.ChildNodes[1])
                        res.Add(new LogItem
                            (
                                this.AttributeByType<int>(node, XMLLogAttributes.ID),
                                this.AttributeByType<string>(node, XMLLogAttributes.Text),
                                this.CheckAttribute(node, XMLLogAttributes.IsPriority.ToString()) ? this.AttributeByType<bool>(node, XMLLogAttributes.IsPriority) : false,
                                this.AttributeByType<DateTime>(node, XMLLogAttributes.Create),
                                this.CheckAttribute(node, XMLLogAttributes.Complite.ToString()) ? this.AttributeByType<DateTime?>(node, XMLLogAttributes.Complite) : null,
                                this.CheckAttribute(node, XMLLogAttributes.DeadLine.ToString()) ? this.AttributeByType<DateTime>(node, XMLLogAttributes.DeadLine) : DateTime.MaxValue,
                                this.CheckAttribute(node, XMLLogAttributes.IsPriority.ToString()) ? projects.FirstOrDefault(x=>x.ID == this.AttributeByType<int>(node, XMLLogAttributes.ProjectID)) : null,
                                Utilities.GetEnum<LogStatuses>(this.AttributeByType<string>(node, XMLLogAttributes.Status) == "true" ? "Complite" : (this.AttributeByType<string>(node, XMLLogAttributes.Status) == "false" ? "Wait" : this.AttributeByType<string>(node, XMLLogAttributes.Status))),
                                node.ChildNodes != null ? GetLog(node) : null,
                                node.ChildNodes != null ? GetLogChanges(node): null
                            ));
                else
                    foreach (XmlNode node in Document.ChildNodes[1])
                        if (this.AttributeByType<int>(node, XMLLogAttributes.ID) == id)
                        {
                            res.Add(new LogItem
                            (
                                this.AttributeByType<int>(node, XMLLogAttributes.ID),
                                this.AttributeByType<string>(node, XMLLogAttributes.Text),
                                this.CheckAttribute(node, XMLLogAttributes.IsPriority.ToString()) ? this.AttributeByType<bool>(node, XMLLogAttributes.IsPriority) : false,
                                this.AttributeByType<DateTime>(node, XMLLogAttributes.Create),
                                this.CheckAttribute(node, XMLLogAttributes.Complite.ToString()) ? this.AttributeByType<DateTime?>(node, XMLLogAttributes.Complite) : null,
                                this.CheckAttribute(node, XMLLogAttributes.DeadLine.ToString()) ? this.AttributeByType<DateTime>(node, XMLLogAttributes.DeadLine) : DateTime.MaxValue,
                                this.CheckAttribute(node, XMLLogAttributes.IsPriority.ToString()) ? projects.FirstOrDefault(x => x.ID == this.AttributeByType<int>(node, XMLLogAttributes.ProjectID)) : null,//null,//this.CheckAttribute(node, XMLLogAttributes.IsPriority.ToString()) ? this.AttributeByType<int>(node, XMLLogAttributes.IsPriority) : 0,
                                Utilities.GetEnum<LogStatuses>(this.AttributeByType<string>(node, XMLLogAttributes.Status) == "true" ? "Complite" : (this.AttributeByType<string>(node, XMLLogAttributes.Status) == "false" ? "Wait" : this.AttributeByType<string>(node, XMLLogAttributes.Status))),
                                node.ChildNodes != null ? GetLog(node) : null,
                                node.ChildNodes != null ? GetLogChanges(node) : null));
                            break;
                        }
            }
            catch 
            {
                this.Error = new Error(ErrorType.Get, new Dictionary<Enum, string>() { 
                    { XMLLogAttributes.ID, id.ToString() }
                });
                DebugLogRepository.Add(InformationType.Error, string.Format(@"LogRepository, Get(id = {0})", id), this.DebugURL); 
            }
            DebugLogRepository.Add(InformationType.Information, string.Format(@"LogRepository, Get(int id = {0}). Success operation", id), this.DebugURL);
            return res.OrderBy(x => x.Status).ThenByDescending(x => x.Create).Reverse().ToList();
        }
        public override bool Create(LogItem item)
        {
            try
            {
                Document.ChildNodes[1].AppendChild(Create_Node(
                    "ItemLog",
                    Create_Attributes(new Dictionary<Enum, string>() { 
                        { XMLLogAttributes.ID, this.lastID.ToString() },
                        { XMLLogAttributes.Text, item.Text },
                        { XMLLogAttributes.IsPriority, item.IsPriority.ToString().ToLower() },
                        { XMLLogAttributes.Create, item.Create.ToString() },
                        { XMLLogAttributes.DeadLine, item.DeadLine.HasValue ? item.DeadLine.ToString() : string.Empty },
                        { XMLLogAttributes.ProjectID, item.Project.ID.ToString() },
                        { XMLLogAttributes.Status, item.Status.ToString() },
                        { XMLLogAttributes.Complite, string.Empty }
                    })
                ));
                DebugLogRepository.Add(InformationType.Information, string.Format(@"LogRepository, Create(LogItem item[ID = {0}]). Success operation", item.ID), this.DebugURL);
                this.lastID++;
            }
            catch
            {
                this.Error = new Error(ErrorType.Create, new Dictionary<Enum, string>() { 
                        { XMLLogAttributes.ID, this.lastID.ToString() },
                        { XMLLogAttributes.Status, item.Status.ToString() },
                        { XMLLogAttributes.Text, item.Text },
                        { XMLLogAttributes.IsPriority, item.IsPriority.ToString().ToLower() },
                        { XMLLogAttributes.Create, item.Create.ToShortDateString() },
                        { XMLLogAttributes.DeadLine, item.DeadLine.HasValue ? item.DeadLine.Value.ToShortDateString() : string.Empty },
                        { XMLLogAttributes.Complite, string.Empty },
                        { XMLLogAttributes.ProjectID, item.Project.ID.ToString() }
                });
                this.Warning = new Warning(
                    WarningType.Create,
                    string.Format("Error creating LogItem"),
                    this.Notify, 
                    delegate(object sender, EventArgs e) {
                        this.Error.Show(DisplayDebugType.MessageBox);
                    });
                DebugLogRepository.Add(InformationType.Error, string.Format(@"LogRepository, Create(LogItem item[ID = {0}])
                                           Error title: {1}
                                           Error message: {2}", item.ID, Error.Title, Error.Message), this.DebugURL);
                return false; 
            }
            DebugLogRepository.Add(InformationType.Information, string.Format(@"LogRepository, Create(LogItem item[ID = {0}]). Success operation", item.ID), this.DebugURL);
            return true;
        }
        public override bool Update(LogItem item, LogItem newitem)
        {
            try
            {
                foreach (XmlNode node in Document.ChildNodes[1])
                    if (this.AttributeByType<int>(node, XMLLogAttributes.ID) == item.ID)
                    {
                        var newnode = Create_Node(
                                "ItemLog",
                                Create_Attributes(new Dictionary<Enum, string>() { 
                                    { XMLLogAttributes.ID, newitem.ID.ToString() },
                                    { XMLLogAttributes.Text, newitem.Text },
                                    { XMLLogAttributes.IsPriority, newitem.IsPriority.ToString().ToLower() },
                                    { XMLLogAttributes.Create, newitem.Create.ToString() },
                                    { XMLLogAttributes.DeadLine, newitem.DeadLine.HasValue ? newitem.DeadLine.ToString() : string.Empty },
                                    { XMLLogAttributes.Status, newitem.Status.ToString() },
                                    { XMLLogAttributes.Complite, newitem.Complite.HasValue ? newitem.Complite.Value.ToString() : string.Empty },
                                    { XMLLogAttributes.ProjectID, newitem.Project != null ? newitem.Project.ID.ToString() : ""}
                                }));
                        if (newitem.Log != null)
                            foreach (var log in newitem.Log)
                                newnode.AppendChild(Create_Node(
                                    "Log",
                                    Create_Attributes(new Dictionary<Enum, string>() {
                                        { XMLLogAttributes.ID, log.ID.ToString() },
                                        { XMLLogAttributes.Type, "datetime" },
                                        { XMLLogAttributes.Date, log.Date.Value.ToShortDateString() },
                                        { XMLLogAttributes.Value, log.Time.GetSecond().ToString() }
                                    })));
                        if (newitem.LogChanges != null)
                            foreach (var log in newitem.LogChanges)
                                newnode.AppendChild(Create_Node(
                                    "Log",
                                    Create_Attributes(new Dictionary<Enum, string>() {
                                        { XMLLogAttributes.ID, log.ID.ToString() },
                                        { XMLLogAttributes.Type, "change" },
                                        { XMLLogAttributes.Date, log.Date.Value.ToString() },
                                        { XMLLogAttributes.Target, log.Target },
                                        { XMLLogAttributes.Value, log.Value }
                                    })));

                        Document.ChildNodes[1].ReplaceChild(newnode, node);
                    }
                DebugLogRepository.Add(
                    InformationType.Information,
                    string.Format(@"LogRepository Update(LogItem item[ID = {0}], LogItem newitem[ID = {1}]). Success operation", item.ID, newitem.ID), this.DebugURL);
                return true;
            }
            catch 
            {
                this.Error = new Error(ErrorType.Update, new Dictionary<Enum, string>() { 
                        { XMLLogAttributes.ID, item.ID.ToString() },
                        { XMLLogAttributes.Status, item.Status.ToString() },
                        { XMLLogAttributes.Text, item.Text },
                        { XMLLogAttributes.Create, item.Create.ToShortDateString() },

                        { XMLLogAttributes.ID, item.ID.ToString() },
                        { XMLLogAttributes.Status, item.Status.ToString() },
                        { XMLLogAttributes.Text, item.Text },
                        { XMLLogAttributes.Create, item.Create.ToShortDateString() },
                });
                this.Warning = new Warning(
                    WarningType.Update, 
                    string.Format("Error updating LogItem"),
                    this.Notify, 
                    delegate(object sender, EventArgs e) {
                        this.Error.Show(DisplayDebugType.MessageBox);
                    });
                DebugLogRepository.Add(InformationType.Error, string.Format(@"LogRepository, Update(LogItem item[ID = {0}], LogItem newitem[ID = {1}])
                                           Error title: {2}
                                           Error message: {3}", item.ID, newitem.ID, Error.Title, Error.Message), this.DebugURL);
                return false; 
            }
        }
        public override bool Delete(int id)
        {
            try
            {
                foreach (XmlNode node in this.Document.ChildNodes[1])
                    if (this.AttributeByType<int>(node, XMLLogAttributes.ID) == id)
                        this.Document.ChildNodes[1].RemoveChild(node);
                DebugLogRepository.Add(InformationType.Information, string.Format(@"LogRepository, Delete(int id = {0}). Success operation", id), this.DebugURL);
                return true;
            }
            catch
            {
                this.Error = new Error(ErrorType.Delete, new Dictionary<Enum, string>() { 
                        { XMLLogAttributes.ID, id.ToString() },
                });
                this.Warning = new Warning(
                    WarningType.Delete,
                    string.Format("Error deleting LogItem"),
                    this.Notify, 
                    delegate(object sender, EventArgs e) {
                        this.Error.Show(DisplayDebugType.MessageBox);
                    });
                DebugLogRepository.Add(InformationType.Error, string.Format(@"LogRepository, Delete(int id = {0})
                                       Error title: {2}
                                       Error message: {3}", id), this.DebugURL);
                return false;
            }
        }

        public bool CopyItemsToNewFile(string newXMLDataURL, List<LogItem> items)
        {
            try
            {
                var newdocument = new XmlDocument();
                if (Utilities.CreateNewXmlDataLogFile(newXMLDataURL))
                    using (var reader = new StreamReader(newXMLDataURL))
                    {
                        newdocument.Load(reader);
                        foreach (var item in items)
                        {
                            var newnode = this.Create_Node(
                                "ItemLog", 
                                Create_Attributes(new Dictionary<Enum,string>() {
                                    { XMLLogAttributes.ID, item.ID.ToString() },
                                    { XMLLogAttributes.Text, item.Text },
                                    { XMLLogAttributes.Create, item.Create.ToShortDateString() },
                                    { XMLLogAttributes.Status, item.Status.ToString() },
                                    { XMLLogAttributes.Complite, item.Complite.HasValue ? item.Complite.Value.ToShortDateString() : string.Empty },
                                },
                                newdocument),
                                newdocument);

                            if (item.Log != null)
                                foreach (var log in item.Log)
                                    newnode.AppendChild(Create_Node(
                                        "Log",
                                        Create_Attributes(new Dictionary<Enum, string>() {
                                            { XMLLogAttributes.Type, "datetime" },
                                            { XMLLogAttributes.Date, log.Date.Value.ToShortDateString() },
                                            { XMLLogAttributes.Value, log.Time.GetSecond().ToString() }
                                        },
                                        newdocument),
                                        newdocument));
                            if (item.LogChanges != null)
                                foreach (var log in item.LogChanges)
                                    newnode.AppendChild(Create_Node(
                                        "Log",
                                        Create_Attributes(new Dictionary<Enum, string>() {
                                            { XMLLogAttributes.Type, "change" },
                                            { XMLLogAttributes.Date, log.Date.Value.ToShortDateString() },
                                            { XMLLogAttributes.Target, log.Target },
                                            { XMLLogAttributes.Value, log.Value }
                                        },
                                        newdocument),
                                        newdocument));
                            newdocument.ChildNodes[1].AppendChild(newnode);
                        }
                    }
                newdocument.Save(newXMLDataURL);
            }
            catch (Exception ex) { return false; }
            return true;
        }
        public bool RemoveItems(List<LogItem> items)
        {
            try
            {
                var ids = items.Select(x => x.ID).ToList();
                var nodes = new List<XmlNode>();
                foreach (XmlNode node in this.Document.ChildNodes[1].ChildNodes)
                    if (ids.Contains(this.AttributeByType<int>(node, XMLLogAttributes.ID)))
                        nodes.Add(node);
                foreach (var node in nodes)
                    this.Document.ChildNodes[1].RemoveChild(node);
            }
            catch { return false; }
            return true;
        }
        private List<LogDate> GetLog(XmlNode item)
        {
            var res = new List<LogDate>();
            foreach (XmlNode child in item.ChildNodes)
                if (this.CheckAttribute(child, XMLLogAttributes.Type.ToString(), "datetime", true) || !this.CheckAttribute(child, XMLLogAttributes.Type.ToString()))
                    res.Add(new LogDate(
                        this.AttributeByType<int>(child, XMLLogAttributes.ID),
                        this.AttributeByType<DateTime>(child, XMLLogAttributes.Date),
                        new LogTime(
                            this.AttributeByType<int>(child, XMLLogAttributes.Value) / 60 / 60,
                            this.AttributeByType<int>(child, XMLLogAttributes.Value) / 60 % 60,
                            this.AttributeByType<int>(child, XMLLogAttributes.Value) % 60
                        )
                    ));
            return res.Count != 0 ? res : null;
        }
        private List<LogChange> GetLogChanges(XmlNode item)
        {
            var res = new List<LogChange>();
            foreach (XmlNode child in item.ChildNodes)
                if (this.CheckAttribute(child, XMLLogAttributes.Type.ToString(), "change", true))
                    res.Add(new LogChange(
                        this.AttributeByType<int>(child, XMLLogAttributes.ID),
                        this.AttributeByType<DateTime>(child, XMLLogAttributes.Date),
                        this.AttributeByType<string>(child, XMLLogAttributes.Target),
                        this.AttributeByType<string>(child, XMLLogAttributes.Value)
                    ));
            return res.Count != 0 ? res : null;
        }

        private int GetLastID()
        {
            var items = Get().Select(x => x.ID).OrderBy(x => x).ToList();
            if (items.Count > 0)
                return items.LastOrDefault() + 1;
            else
                return 0;
        }
    }
}
